﻿using Core.DataAccess.Model;
using Core.DataAccess.Model.iTool.Gateway;
using Core.Framework;
using Environment.Interfaces;
using HandlerContextOptions;
using iTool.ClusterComponent;
using iTool.Dashboard.EmbeddedAssets;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Environment.Implements
{
    public class ToolCenter : IToolCenter
    {
        INodeService nodeService;

        private int inSubmitLinuxStatus = 0;

        public ToolCenter(INodeService nodeService)
        {
            this.nodeService = nodeService;
        }

        public async Task<iToolCenterOptionResult> Edit(iToolCenterOptionRequset options)
        {
            try
            {
                iToolServiceConfiguration config;
                List<Node> nodes;

                await using (iToolGatewayContext context = new iToolGatewayContext())
                {
                    config = await context.iToolServiceConfigurations.FirstOrDefaultAsync();

                    if (config == null)
                    {
                        config = new iToolServiceConfiguration
                        {
                            Configuration = options.TryToJson(),
                            CreateTime = DateTime.Now
                        };

                        await context.iToolServiceConfigurations.AddAsync(config);
                    }
                    else
                    {
                        config.Configuration = options.TryToJson();
                    }

                    await context.SaveChangesAsync();

                    nodes = await context.Nodes.ToListAsyncOfAsNoTracking();
                }

                // 更新各探测器（如果探测器在更新之后上线。  那么 探测器则主动获取）
                var iToolConfigure = await this.GetiToolConfigure(options);

                AppSetting.SetiToolLoggerOptions(iToolConfigure.LoggerDatabases.First());

                // 更新所有节点
                if (nodes?.Count > 0)
                {
                    var iToolConfigureString = iToolConfigure.TryToJson();
                    if (!string.IsNullOrWhiteSpace(iToolConfigureString))
                    {
                        iToolConfigureString = iToolConfigureString.Replace("127.0.0.1", AppSetting.GatewayPublicIp);

                        foreach (var item in nodes)
                        {
                            await this.nodeService.PostAsync(item.IP, "iTool/config", new StringContent(iToolConfigureString));
                        }
                    }
                }

                return new iToolCenterOptionResult
                {
                    IsSuccessful = true
                };

            }
            catch (Exception ex)
            {
                return new iToolCenterOptionResult
                {
                    IsSuccessful = false,
                    Description = ex.Message
                };
            }
        }

        public async Task<iToolCenterOptionRequset> Get()
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var config = await context.iToolServiceConfigurations.FirstOrDefaultAsync();

                if (config == null)
                {
                    return default;
                }
                else
                {
                    return config.Configuration?.TryToEntity<iToolCenterOptionRequset>();
                }
            }
        }

        public async Task<iToolConfigure> GetiToolConfigure(iToolCenterOptionRequset options = null)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var config = await context.iToolServiceConfigurations.FirstOrDefaultAsync();

                if (config == null)
                {
                    return default;
                }

                options = config.Configuration?.TryToEntity<iToolCenterOptionRequset>();
            }

            if (options != null)
            {
                List<int> serviceInstanceIds = new List<int>();
                List<ServiceInstance> serviceInstances = new List<ServiceInstance>();
                var iToolBaseServiceInstanceIds = options.BaseSQLServer.Select(item => int.Parse(item));
                var FunctionServiceInstanceIds = options.FnsSQLServer.Select(item => int.Parse(item));
                var LoggerServiceInstanceIds = options.LogSQLServer.Select(item => int.Parse(item));
                var MessageServiceInstanceIds = options.MsgSQLServer.Select(item => int.Parse(item));
                var MongoDBServiceInstanceIds = options.MongoDB.Select(item => int.Parse(item));
                var ElasticSearchServiceInstanceIds = options.ElasticSearch.Select(item => int.Parse(item));

                serviceInstanceIds = serviceInstanceIds.Concat(iToolBaseServiceInstanceIds).ToList();
                serviceInstanceIds = serviceInstanceIds.Concat(FunctionServiceInstanceIds).ToList();
                serviceInstanceIds = serviceInstanceIds.Concat(LoggerServiceInstanceIds).ToList();
                serviceInstanceIds = serviceInstanceIds.Concat(MessageServiceInstanceIds).ToList();
                serviceInstanceIds = serviceInstanceIds.Concat(MongoDBServiceInstanceIds).ToList();
                serviceInstanceIds = serviceInstanceIds.Concat(ElasticSearchServiceInstanceIds).ToList();

                await using (iToolGatewayContext context = new iToolGatewayContext())
                {
                    serviceInstances = await context.ServiceInstances.Where(item => serviceInstanceIds.Contains(item.ServiceInstanceId)).ToListAsyncOfAsNoTracking();
                }

                var iToolConfigure = new iToolConfigure
                {
                    Port = options.Port,
                    ServerPort = options.ServicePort,
                    ServerGatewayPort = options.ServiceGatewayPort,
                    AcceptQueueCount = options.SoBacklog,
                    HeartbeatTimes = options.HeartbeatTimeOutSeconds,

                    iToolBaseDatabaseList = serviceInstances
                        .Where(item => iToolBaseServiceInstanceIds.Contains(item.ServiceInstanceId))
                        .Select(item => new AdoNetOptions
                        {
                            DataSource = $"{item.NodeIP},{item.Port}",
                            PWD = item.Password,
                            UID = "sa"
                        }),
                    iToolBaseDatabases = serviceInstances
                        .Where(item => iToolBaseServiceInstanceIds.Contains(item.ServiceInstanceId))
                        .Select(item => $"Data Source={item.NodeIP},{item.Port};database=iTool.CloudBase;uid=sa;pwd={item.Password};MultipleActiveResultSets=true;"),
                    FunctionDatabases = serviceInstances
                        .Where(item => FunctionServiceInstanceIds.Contains(item.ServiceInstanceId))
                        .Select(item => $"Data Source={item.NodeIP},{item.Port};database=iTool.Function;uid=sa;pwd={item.Password};MultipleActiveResultSets=true;"),
                    MessageDatabases = serviceInstances
                        .Where(item => MessageServiceInstanceIds.Contains(item.ServiceInstanceId))
                        .Select(item => $"Data Source={item.NodeIP},{item.Port};database=iTool.Message;uid=sa;pwd={item.Password};MultipleActiveResultSets=true;"),
                    LoggerDatabases = serviceInstances
                        .Where(item => LoggerServiceInstanceIds.Contains(item.ServiceInstanceId))
                        .Select(item => $"Data Source={item.NodeIP},{item.Port};database=iTool.Logger;uid=sa;pwd={item.Password};MultipleActiveResultSets=true;"),
                    MongoDBProviders = serviceInstances
                        .Where(item => MongoDBServiceInstanceIds.Contains(item.ServiceInstanceId))
                        .Select(item => $"mongodb://{item.User}:{item.Password}@{item.NodeIP}:{item.Port}/iToolFiles?authSource=admin"),
                    ElasticSearchProviders = serviceInstances
                        .Where(item => ElasticSearchServiceInstanceIds.Contains(item.ServiceInstanceId))
                        .Select(item => new ElasticSearchProvider { Ip = item.NodeIP, Port = item.Port, User = item.User, Password = item.Password }),

                };
                return iToolConfigure;
            }
            else
            {
                return default;
            }
        }

        public async Task<bool> IsAvailableClouds()
        {
            try
            {
                IClusterService clusterService = ServerProvider.GetService<IClusterService>("IDashboardService");

                if (clusterService == null)
                {
                    if (Interlocked.Exchange(ref inSubmitLinuxStatus, 1) == 0)
                    {
                        await using (iToolGatewayContext context = new iToolGatewayContext())
                        {
                            bool isAvailableClouds = await context.ServiceInstances.Where(item => item.Title == "iTool-Server" && item.Status == 2).AnyAsync();

                            var configOptions = await this.Get() ?? new iToolCenterOptionRequset();

                            if (configOptions.BaseSQLServer.Any())
                            {
                                int.TryParse(configOptions.BaseSQLServer.First(), out int instanceId);
                                var instance = await context.ServiceInstances.Where(item => item.ServiceInstanceId == instanceId).FirstOrDefaultAsync();

                                if (instance != null)
                                {
                                    var builder = new iToolClientBuilder();
                                    builder.UseAdoNetClustering(new AdoNetClusterOptions
                                    {
                                        AdoNetOptions = new AdoNetOptions
                                        {
                                            // Test
                                            // DataSource = $"1.117.167.53,{instance.Port}",
                                            DataSource = $"{instance.NodeIP},{instance.Port}",
                                            UID = "sa",
                                            PWD = instance.Password
                                        },
                                        ClusterOptions = new ClusterIdentificationOptions
                                        {
                                            ClusterId = "iToolServiceCluster",
                                            ServiceId = "iToolService"
                                        },
                                        ResponseTimeout = TimeSpan.FromSeconds(15)
                                    });

                                    await builder.BuildAndConnectAsync(new ServiceCollection());
                                }
                                else
                                {
                                    ServerProvider.GetServiceCollection("IDashboardService", new ServiceCollection());
                                    Interlocked.Exchange(ref inSubmitLinuxStatus, 0);
                                    return false;
                                }
                            }

                            Interlocked.Exchange(ref inSubmitLinuxStatus, 0);
                            return isAvailableClouds;
                        }
                    }
                    else
                    {
                        return false;
                    }

                }
                else
                {
                    try
                    {
                        var keyvalue = clusterService.GetService<IKeyValueStorageService>("gateway_ping");
                        await keyvalue.GetAsync();
                        return true;
                    }
                    catch (Exception)
                    {
                        ServerProvider.GetServiceCollection("IDashboardService", new ServiceCollection());
                        Interlocked.Exchange(ref inSubmitLinuxStatus, 0);
                        return false;
                    }
                }
            }
            catch (Exception)
            {
                Interlocked.Exchange(ref inSubmitLinuxStatus, 0);
                return false;
            }
        }
    }
}
